package com.xiyu.service.service.customer;

import com.xiyu.service.model.system.dept.SystemDept;
import com.xiyu.service.repository.system.user.SystemUserRepository;
import com.xiyu.service.service.system.dept.DeptService;
import com.xiyu.service.service.system.user.UserService;
import com.xiyu.service.vo.customer.customer.CustomerTransferInput;
import com.xiyu.service.vo.customer.customer.CustomerGetMailInfoByCustomerIdsOutput;
import com.xiyu.service.vo.customer.customer.CustomerGetMailInfoByCustomerIdsInput;
import com.xiyu.service.model.customer.follow.CustomerFollowDraft;
import com.xiyu.service.model.customer.follow.CustomerFollow;
import com.xiyu.service.repository.customer.follow.CustomerFollowRepository;
import com.xiyu.service.vo.customer.customer.CustomerCreateFollowInput;
import com.xiyu.service.service.setting.BusinessFieldService;
import com.xiyu.service.vo.customer.customer.CustomerPutInPoolOutput;
import com.xiyu.service.vo.customer.customer.CustomerPutInPoolInput;
import com.xiyu.service.vo.customer.contact.baseVO.CustomerContactBase;
import com.xiyu.service.vo.customer.contact.baseVO.CustomerContactMailBase;
import com.xiyu.service.vo.customer.contact.baseVO.CustomerContactPhoneBase;
import com.xiyu.service.vo.customer.contact.baseVO.CustomerContactSocialMediaBase;
import com.xiyu.service.vo.customer.customer.CustomerSingleGetDetailOutput;
import com.xiyu.service.enums.system.social.SocialTypeEnum;
import com.xiyu.service.model.customer.contact.*;
import com.xiyu.service.repository.customer.contact.*;
import com.xiyu.service.model.customer.info.CustomerImage;
import com.xiyu.service.repository.customer.info.CustomerImageRepository;
import com.xiyu.service.model.customer.info.CustomerWebsite;
import com.xiyu.service.repository.customer.info.CustomerWebsiteRepository;
import com.xiyu.service.service.infra.data.DictNoService;
import com.xiyu.service.vo.customer.customer.CustomerUpdateInput;
import com.xiyu.service.model.customer.info.CustomerInfoProps;
import com.xiyu.service.model.customer.info.CustomerInfoDraft;
import com.xiyu.service.vo.customer.customer.CustomerCreateInput;
import com.xiyu.service.model.customer.info.CustomerInfo;
import com.xiyu.service.repository.customer.info.CustomerInfoRepository;
import com.xiyu.service.vo.customer.customer.CustomerPageQueryOutput;
import com.xiyu.service.vo.customer.customer.CustomerPageQueryInput;
import com.xiyu.service.vo.customer.info.baseVO.CustomerWebsiteBase;
import org.springframework.stereotype.Service;
import jakarta.annotation.Resource;
import org.springframework.validation.annotation.Validated;
import org.babyfish.jimmer.Page;
import java.util.*;

import org.springframework.transaction.annotation.Transactional;
import com.xiyu.service.util.entity.EntityUtils;
import org.babyfish.jimmer.DraftObjects;
import com.xiyu.service.framework.web.web.core.pojo.PageResult;
import com.xiyu.service.convert.customer.CustomerConvert;

import java.util.AbstractMap.SimpleEntry;

import static com.xiyu.service.framework.exception.util.ServiceExceptionUtil.exception;
import static com.xiyu.service.errorCode.customer.CustomerErrorCode.*;
import static com.xiyu.service.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;

import java.util.stream.Collectors;

/**
 * 客户管理 Service 实现类
 */
@Service
@Validated
public class CustomerServiceImpl implements CustomerService {

    @Resource
    private CustomerFollowRepository customerFollowRepository;


    @Resource
    private CustomerContactRepository customerContactRepository;

    @Resource
    private CustomerImageRepository customerImageRepository;

    @Resource
    private CustomerWebsiteRepository customerWebsiteRepository;

    @Resource
    private CustomerInfoRepository customerInfoRepository;

    @Resource
    private CustomerContactBusinessCardRepository customerContactBusinessCardRepository;

    @Resource
    private CustomerContactMailRepository customerContactMailRepository;

    @Resource
    private CustomerContactPhoneRepository customerContactPhoneRepository;

    @Resource
    private CustomerContactSocialMediaRepository customerContactSocialMediaRepository;

    @Resource
    private DictNoService dictNoService;

    @Resource
    private UserService userService;

    @Resource
    private DeptService deptService;

    @Resource
    private SystemUserRepository systemUserRepository;


    @Resource
    private BusinessFieldService businessFieldService;

    private static final List<String> socialTypeName = List.of("WeChat", "QQ", "FaceBook", "Instagram", "YouTube", "Telegram", "Twitter", "Skype", "LinkedIn", "ZALO", "Viber", "旺旺", "钉钉", "Tiktok", "Line", "VK");


    @Override
    public PageResult<CustomerPageQueryOutput> pageQuery(CustomerPageQueryInput inputVO) {

        Long userId = getLoginUserId();
        if(Objects.equals(userId, null))
            return null;
        List<Long> ownerUserIds = userService.getOwnerUserIds(userId);
        Long deptId = systemUserRepository.findById(userId).get().deptId();
        List<Long> ownerDeptIds = deptService.getDeptListByParentId(deptId, true).stream().map(SystemDept::id).collect(Collectors.toList());
        boolean isLeader = deptService.isLeaderUser(userId);

        Page<CustomerInfo> pageCustomerInfo = customerInfoRepository.pageQuery(inputVO, ownerUserIds, ownerDeptIds, isLeader);
        List<CustomerPageQueryOutput> listCustomerInfo = CustomerConvert.INSTANCE.pageQueryOutputConvert(pageCustomerInfo.getRows());
        return new PageResult<>(listCustomerInfo, pageCustomerInfo.getTotalRowCount());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long create(CustomerCreateInput inputVO) {
        // 客户的名称不能重复
        Optional<CustomerInfo> duplicateInfos = customerInfoRepository.findByName(inputVO.getName());
        if(duplicateInfos.isPresent()){
            throw exception(CUSTOMER_INFO_NAME_DUPLICATE);
        }
        // 客户联系人的姓名不能重复
        verifyContactName(inputVO.getContacts());

        // 客户网址不能重复
        verifyCustomerWebsite(inputVO.getWebsites());

        // 联系人里邮箱、社交媒体、手机号不能重复
        for(CustomerCreateInput.contact contact : inputVO.getContacts()){
            verifyCreateContact(contact);
        }

        CustomerInfo newCustomerInfo = CustomerConvert.INSTANCE.createInputConvert(inputVO);
        newCustomerInfo = CustomerInfoDraft.$.produce(newCustomerInfo, draft ->
                draft.setCustomerNo(dictNoService.produceNo("customer"))
                        .setMainContactId(0L).setIsFallPool(false).setComingFallPoolRuleId(0L));

        // 生成客户编号
        newCustomerInfo = customerInfoRepository.insert(newCustomerInfo);
        // 更新主联系人
        CustomerContact mainContact = customerContactRepository.findMailContact(newCustomerInfo.id());
        customerInfoRepository.updateMainContactId(newCustomerInfo.id(), mainContact.id());
        return newCustomerInfo.id();

    }

    private void verifyContactName(List<? extends CustomerContactBase> contacts){
        Map<String, Long> nameCounts = contacts.stream()
                .collect(Collectors.groupingBy(CustomerContactBase::getName, Collectors.counting()));

        List<String> duplicateNames = nameCounts.entrySet().stream()
                .filter(entry -> entry.getValue() > 1)
                .map(Map.Entry::getKey)
                .collect(Collectors.toList());
        if(!duplicateNames.isEmpty()){
            throw exception(CUSTOMER_CONTACT_NAME_DUPLICATE, duplicateNames.get(0));
        }
    }

    private void verifyCustomerWebsite(List<? extends CustomerWebsiteBase> websites){
        Map<String, Long> websiteCounts = websites.stream()
                .collect(Collectors.groupingBy(CustomerWebsiteBase::getWebsite, Collectors.counting()));

        List<String> duplicateWebsites = websiteCounts.entrySet().stream()
                .filter(entry -> entry.getValue() > 1)
                .map(Map.Entry::getKey)
                .collect(Collectors.toList());
        if(!duplicateWebsites.isEmpty()){
            throw exception(CUSTOMER_WEBSITE_DUPLICATE, duplicateWebsites.get(0));
        }
    }

    private void verifyContactMail(List<? extends CustomerContactMailBase> mails, String contactName){
        // 邮箱校验
        Map<String, Long> mailCounts = mails.stream()
                .collect(Collectors.groupingBy(CustomerContactMailBase::getMailAddress, Collectors.counting()));

        List<String> duplicateMails = mailCounts.entrySet().stream()
                .filter(entry -> entry.getValue() > 1)
                .map(Map.Entry::getKey)
                .collect(Collectors.toList());
        if(!duplicateMails.isEmpty()){
            throw exception(CUSTOMER_CONTACT_MAIL_NAME_DUPLICATE, contactName, duplicateMails.get(0));
        }
    }

    private void verifyContactPhone(List<? extends CustomerContactPhoneBase> phones, String contactName){
        Map<String, Long> phoneCounts = phones.stream()
                .collect(Collectors.groupingBy(CustomerContactPhoneBase::getPhoneNo, Collectors.counting()));

        List<String> duplicatePhones = phoneCounts.entrySet().stream()
                .filter(entry -> entry.getValue() > 1)
                .map(Map.Entry::getKey)
                .collect(Collectors.toList());
        if(!duplicatePhones.isEmpty()){
            throw exception(CUSTOMER_CONTACT_PHONE_NAME_DUPLICATE, contactName, duplicatePhones.get(0));
        }
    }

    private void verifySocialMedia(List<? extends CustomerContactSocialMediaBase> socialMedias, String contactName){
        Map<SimpleEntry<Integer, String>, Long> socialMediaCounts = socialMedias.stream()
                .collect(Collectors.groupingBy(concat -> new SimpleEntry<>(concat.getSocialType(), concat.getSocialAccount()), Collectors.counting()));

        List<SimpleEntry<Integer, String>> duplicateSocialMedias = socialMediaCounts.entrySet().stream()
                .filter(entry -> entry.getValue() > 1)
                .map(Map.Entry::getKey)
                .collect(Collectors.toList());
        if(!duplicateSocialMedias.isEmpty()){
            throw exception(CUSTOMER_CONTACT_SOCIAL_MEDIA_NAME_DUPLICATE, contactName,
                    socialTypeName.get(duplicateSocialMedias.get(0).getKey()),
                    duplicateSocialMedias.get(0).getValue());
        }
    }

    private void verifyCreateContact(CustomerCreateInput.contact contact){
        // 邮箱校验
        verifyContactMail(contact.getMails(), contact.getName());

        // 手机号校验
        verifyContactPhone(contact.getPhones(), contact.getName());

        // 社交媒体校验
        verifySocialMedia(contact.getSocialMedias(), contact.getName());
    }

    private void verifyUpdateContact(CustomerUpdateInput.contact contact){
        // 邮箱校验
        verifyContactMail(contact.getMails(), contact.getName());

        // 手机号校验
        verifyContactPhone(contact.getPhones(), contact.getName());

        // 社交媒体校验
        verifySocialMedia(contact.getSocialMedias(), contact.getName());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean update(CustomerUpdateInput inputVO) {
        Optional<CustomerInfo> optionalOldCustomerInfo = customerInfoRepository.findById(inputVO.getId());
        if(optionalOldCustomerInfo.isEmpty()){
            throw exception(CUSTOMER_INFO_NOT_EXIST);
        }
        // 客户的名称不能重复
        Optional<CustomerInfo> duplicateInfo = customerInfoRepository.findByName(inputVO.getName());
        if(duplicateInfo.isPresent() && duplicateInfo.get().id() != inputVO.getId()){
            throw exception(CUSTOMER_INFO_NAME_DUPLICATE);
        }
        // 更新图片
        updateCustomerImages(inputVO.getId(), inputVO.getImages());

        // 更新网址
        updateCustomerWebsites(inputVO.getId(), inputVO.getWebsites());


        // 客户联系人的姓名不能重复
        verifyContactName(inputVO.getContacts().stream().filter(contact -> !Objects.equals(contact.getOperateType(), "delete")).collect(Collectors.toList()));

        //更新联系人
        for(CustomerUpdateInput.contact contact : inputVO.getContacts()){
            if(Objects.equals(contact.getOperateType(), "delete")){
                Optional<CustomerContact> optionalDeleteCustomerContact = customerContactRepository.findById(contact.getId());
                if(optionalDeleteCustomerContact.isEmpty()){
                    throw exception(CUSTOMER_WEBSITE_NOT_EXIST);
                }
                customerContactRepository.updateDeleteTime(contact.getId());
            }else if(Objects.equals(contact.getOperateType(), "new")){
                verifyUpdateContact(contact);
                CustomerContact newCustomerContact = CustomerConvert.INSTANCE.updateInputConvert(contact);
                customerContactRepository.insert(newCustomerContact);
            }else{
                Optional<CustomerContact> optionalOldCustomerContact = customerContactRepository.findById(contact.getId());
                if(optionalOldCustomerContact.isEmpty()) {
                    throw exception(CUSTOMER_WEBSITE_NOT_EXIST);
                }
                verifyUpdateContact(contact);
                // 更新邮箱
                updateContactMails(contact.getId(), contact.getMails());
                // 更新手机号
                updateContactPhone(contact.getId(), contact.getPhones());
                // 更新社交媒体
                updateContactSocialMedia(contact.getId(), contact.getSocialMedias());
                // 更新名片
                updateContactBusinessCard(contact.getId(), contact.getBusinessCards());

                CustomerContact updateCustomerContact = CustomerConvert.INSTANCE.updateInputConvert(contact);
                updateCustomerContact = CustomerContactDraft.$.produce(updateCustomerContact, draft -> {
                    DraftObjects.unload(draft, CustomerContactProps.MAILS);
                    DraftObjects.unload(draft, CustomerContactProps.PHONES);
                    DraftObjects.unload(draft, CustomerContactProps.SOCIAL_MEDIAS);
                    DraftObjects.unload(draft, CustomerContactProps.BUSINESS_CARDS);
                });
                if (!EntityUtils.isEquals(optionalOldCustomerContact.get(), updateCustomerContact))
                    customerContactRepository.update(updateCustomerContact);
            }
        }
        CustomerInfo updateCustomerInfo = CustomerConvert.INSTANCE.updateInputConvert(inputVO);
        updateCustomerInfo = CustomerInfoDraft.$.produce(updateCustomerInfo, draft -> {
            DraftObjects.unload(draft, CustomerInfoProps.CONTACTS);
            DraftObjects.unload(draft, CustomerInfoProps.IMAGES);
            DraftObjects.unload(draft, CustomerInfoProps.WEBSITES);
        });
        if (!EntityUtils.isEquals(optionalOldCustomerInfo.get(), updateCustomerInfo))
            customerInfoRepository.update(updateCustomerInfo);
        // 更新主联系人
        CustomerContact mainContact = customerContactRepository.findMailContact(updateCustomerInfo.id());
        customerInfoRepository.updateMainContactId(updateCustomerInfo.id(), mainContact.id());
        return true;
    }

    private void updateCustomerImages(Long customerId, List<CustomerUpdateInput.image> images){
        List<Long> updateIds = images.stream().map(CustomerUpdateInput.image::getId).filter(Objects::nonNull).collect(Collectors.toList());
        customerImageRepository.deleteByExcludeId(customerId, updateIds);
        images.forEach(image -> {
            image.setCustomerId(customerId);
            CustomerImage customerImage = CustomerConvert.INSTANCE.updateInputConvert(image);
            if(image.getId() == null)
                customerImageRepository.insert(customerImage);
            else
                customerImageRepository.update(customerImage);
        });

    }

    private void updateCustomerWebsites(Long customerId, List<CustomerUpdateInput.website> websites){
        List<Long> updateIds = websites.stream().map(CustomerUpdateInput.website::getId).filter(Objects::nonNull).collect(Collectors.toList());
        customerWebsiteRepository.deleteByExcludeId(customerId, updateIds);
        websites.forEach(website -> {
            website.setCustomerId(customerId);
            CustomerWebsite customerImage = CustomerConvert.INSTANCE.updateInputConvert(website);
            if(website.getId() == null)
                customerWebsiteRepository.insert(customerImage);
            else
                customerWebsiteRepository.update(customerImage);
        });

    }

    private void updateContactMails(Long contactId, List<CustomerUpdateInput.mail> mails){
        List<Long> updateIds = mails.stream().map(CustomerUpdateInput.mail::getId).filter(Objects::nonNull).collect(Collectors.toList());
        customerContactMailRepository.deleteByExcludeId(contactId, updateIds);
        mails.forEach(mail -> {
            mail.setContactId(contactId);
            CustomerContactMail contactMail = CustomerConvert.INSTANCE.updateInputConvert(mail);
            if(mail.getId() == null)
                customerContactMailRepository.insert(contactMail);
            else
                customerContactMailRepository.update(contactMail);
        });

    }

    private void updateContactPhone(Long contactId, List<CustomerUpdateInput.phone> phones){
        List<Long> updateIds = phones.stream().map(CustomerUpdateInput.phone::getId).filter(Objects::nonNull).collect(Collectors.toList());
        customerContactPhoneRepository.deleteByExcludeId(contactId, updateIds);
        phones.forEach(phone -> {
            phone.setContactId(contactId);
            CustomerContactPhone contactPhone = CustomerConvert.INSTANCE.updateInputConvert(phone);
            if(phone.getId() == null)
                customerContactPhoneRepository.insert(contactPhone);
            else
                customerContactPhoneRepository.update(contactPhone);
        });
    }

    private void updateContactSocialMedia(Long contactId, List<CustomerUpdateInput.socialMedia> socialMedias){
        List<Long> updateIds = socialMedias.stream().map(CustomerUpdateInput.socialMedia::getId).filter(Objects::nonNull).collect(Collectors.toList());
        customerContactSocialMediaRepository.deleteByExcludeId(contactId, updateIds);
        socialMedias.forEach(socialMedia -> {
            socialMedia.setContactId(contactId);
            CustomerContactSocialMedia contactSocialMedia = CustomerConvert.INSTANCE.updateInputConvert(socialMedia);
            if(socialMedia.getId() == null)
                customerContactSocialMediaRepository.insert(contactSocialMedia);
            else
                customerContactSocialMediaRepository.update(contactSocialMedia);
        });
    }

    private void updateContactBusinessCard(Long contactId, List<CustomerUpdateInput.businessCard> businessCards){
        List<Long> updateIds = businessCards.stream().map(CustomerUpdateInput.businessCard::getId).filter(Objects::nonNull).collect(Collectors.toList());
        customerContactBusinessCardRepository.deleteByExcludeId(contactId, updateIds);
        businessCards.forEach(businessCard -> {
            businessCard.setContactId(contactId);
            CustomerContactBusinessCard contactBusinessCard = CustomerConvert.INSTANCE.updateInputConvert(businessCard);
            if(businessCard.getId() == null)
                customerContactBusinessCardRepository.insert(contactBusinessCard);
            else
                customerContactBusinessCardRepository.update(contactBusinessCard);
        });
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean setRecycle(List<Long> ids) {
        customerInfoRepository.batchUpdateIsRecycle(ids, true);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean restore(List<Long> ids) {
        customerInfoRepository.batchUpdateIsRecycle(ids, false);
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean deleted(List<Long> ids) {
        customerInfoRepository.batchUpdateDeletedTime(ids);
        return null;
    }

    @Override
    public CustomerSingleGetDetailOutput singleGetDetail(Long id) {
        Optional<CustomerInfo> optionCustomerInfo = customerInfoRepository.singleGetDetail(id);
        if(optionCustomerInfo.isEmpty()){
            throw exception(CUSTOMER_INFO_NOT_EXIST);
        }
        return CustomerConvert.INSTANCE.singleGetDetailOutputConvert(optionCustomerInfo.get());

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<CustomerPutInPoolOutput> putInPool(CustomerPutInPoolInput inputVO) {
        Long stateDealOptionId = businessFieldService.getStateDealOptionId();
        List<CustomerPutInPoolOutput> errorMessages = new ArrayList<>();
        for(Long id : inputVO.getIds()){

            CustomerInfo customerInfo = customerInfoRepository.findNullable(id);
            if(customerInfo == null){
                errorMessages.add(new CustomerPutInPoolOutput(id, "", "", CUSTOMER_INFO_NOT_EXIST.getMsg()));
                continue;
            }
            // 成交状态的客户不能放入公海
            if(customerInfo.state().equals(stateDealOptionId)){
                errorMessages.add(new CustomerPutInPoolOutput(id, customerInfo.customerNo(), customerInfo.name(), CUSTOMER_NO_DEAL_STATUS_PUT_IN_POOL.getMsg()));
                continue;
            }
            // 判断客户是否已经在公海
            if(customerInfo.isFallPool().equals(true)){
                errorMessages.add(new CustomerPutInPoolOutput(id, customerInfo.customerNo(), customerInfo.name(), CUSTOMER_ALREADY_PUT_IN_POOL.getMsg()));
                continue;
            }

            customerInfoRepository.updateFallInPool(id, inputVO.getFallPoolReason());
        }

        return errorMessages;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createFollow(CustomerCreateFollowInput inputVO) {
        CustomerFollow newCustomerFollow = CustomerConvert.INSTANCE.createFollowInputConvert(inputVO);
        newCustomerFollow = CustomerFollowDraft.$.produce(newCustomerFollow, draft -> draft.setFollowNo(dictNoService.produceNo("customerFollow")));
        newCustomerFollow = customerFollowRepository.insert(newCustomerFollow);
        customerInfoRepository.updateLastFollowTime(inputVO.getCustomerId());
        return newCustomerFollow.id();

    }

    @Override
    public List<CustomerGetMailInfoByCustomerIdsOutput> getMailInfoByCustomerIds(CustomerGetMailInfoByCustomerIdsInput inputVO) {
        return CustomerConvert.INSTANCE.getMailInfoByCustomerIdsOutputConvert(customerContactMailRepository.getMailInfoByCustomerIds(inputVO.getIds()));
    }

    @Override
    public Boolean transfer(CustomerTransferInput inputVO) {
        customerInfoRepository.updateOwnerId(inputVO.getId(), inputVO.getOwnerId());
        return true;
    }

}
